package com.qf.smartdevice.webscoket;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.qf.smartdevice.event.DevicesCacheChangeEvent;
import com.qf.smartdevice.task.CheckDevicesStatusTask;
import org.aspectj.lang.annotation.AfterThrowing;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.BinaryMessage;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Component
@EnableWebSocket//开启 websocket

public class MyBaseWebsocketHandler extends TextWebSocketHandler {


    private ObjectMapper objectMapper = new ObjectMapper();
    //我们的目的是通过当前webscoket 给指定的设备推送命令
    // 哪个设备   是一个设备嘛 很多个设备
    // 每个设备的会话是什么
    // 保存的是所有的设备的会话
    protected Map<String, WebSocketSession> allClients = new HashMap<>();
    //  记录设备最后一次连接时间的
    protected Map<String, Long> lastConnectionTime = new HashMap<>();

    protected ApplicationContext context;

    @Autowired
    public void setContext(ApplicationContext context) {
        this.context = context;
    }

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        //当设备和服务器建立连接的时候执行  保存会话
        //我们约定地址的最后一位是设备的编码
//      获取客户端发来的 路径 包括名字
        String uri = session.getUri().toString();
//        获取路径后面的名字
        String uuid = uri.substring(uri.lastIndexOf("/") + 1);
//      为了方便后面随时获取 uuid,我们保存了一份
        session.getAttributes().put("uuid", uuid);
        allClients.put(uuid, session);
        System.out.println("收到 websocket 请求了" + uuid);
//        保存设备的连接时间
        lastConnectionTime.put(uuid, System.currentTimeMillis());

//         当前方法执行的时候代表设备连接到服务器了 需要更新状态  需要修改设备在数据库中的状态为现在
//         当前设备是谁  我们要求设备以UUID  作为标识来连接我们的websocket 所以上面UUID 就是设备的UUID
//         我们可以 以UUID 作为条件更新设备的在线状态
//         更新状态和 websocket 建立连接这两个操作有关系吗  更新数据库数失败要不要将设备下线  不需要 也就是说我们

        context.publishEvent(DevicesCacheChangeEvent.onLine(uuid));
    }


    /*
     * 如果设备因断电断网非正常原因导致的断开连接 服务器不会受到断开的操作  所以该方法在非正常的情况下不会执行
     * 我们可以通过记录设备的最后一次心跳时间的方式来判断 比如超过一分钟没有发送过心跳数据的我们就认为你掉线
     * 所以需要记录设备的最后一次心跳时间 每次心跳都需要更新时间的 什么时候有心跳 我们可以认为心跳就是设备向服务器发送了一个数据
     *但是设备什么时候掉线也不确定 在任何时间点都可能会掉线 所以要循环检查
     *  */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        String uuid = (String) session.getAttributes().get("uuid");
        // 当会话关闭的时候 从所有的客户端会话保存的map 中删除
        allClients.remove(uuid);
        System.err.println("设备断开了连接" + uuid);
        context.publishEvent(DevicesCacheChangeEvent.offLine(uuid));


    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {

        /*
         * 发送指令给设备
         * 收到消息后直接发送指令给设备
         * 暂时约定 前端传递{"touser":"shebeiid,"msg":"mingling"}
         * */
        String uuid = (String) session.getAttributes().get("uuid");
//        设备每发送一次数据我们就更新一次时间 所以必须有个地方保存了更新时间
        System.err.println("收到设备心跳数据了");
        lastConnectionTime.put(uuid, System.currentTimeMillis());
    }

    @EventListener
    @Async
    public void onEvent(CheckDevicesStatusTask event) {

        if (!isNeedCheckStatus()) {//不需要检查心跳
            return;
        }
        List<String> list = lastConnectionTime.keySet().stream().filter(uuid -> {

//            获取当前时间
            long currentTimeMillis = System.currentTimeMillis();
            //获取当前设备的上次心跳时间
            Long time = lastConnectionTime.get(uuid);

            if (currentTimeMillis - time >= 60000) {
//            当前时间和上次的心跳时间已经超出了一分钟
                WebSocketSession session = allClients.get(uuid);
//               因为设备已经离线了所以从保存有所有会话的 map 中删除数据
                allClients.remove(uuid);
                if (session != null && session.isOpen()) {
                    try {
                        session.close();
                        session = null;
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            return currentTimeMillis - time >= 60000;
        }).collect(Collectors.toList());

//        遍历 所有离弦的设备的UUID 集合  然后从lastConnectionTime删除掉指定的数据
        list.forEach(uuid -> {
            lastConnectionTime.remove(uuid);
        });
    }

    private boolean isNeedCheckStatus() {
        return true;
    }

}
